import 'package:json_annotation/json_annotation.dart';

part 'download_task.g.dart';

enum DownloadStatus {
  @JsonValue('pending')
  pending,
  @JsonValue('downloading')
  downloading,
  @JsonValue('completed')
  completed,
  @JsonValue('failed')
  failed,
  @JsonValue('cancelled')
  cancelled,
  @JsonValue('paused')
  paused,
}

@JsonSerializable()
class DownloadTask {
  final String id;
  final String fileName;
  final String cloudId;
  final String downloadUrl;
  final String savePath;
  final int fileSize;
  final String md5;
  final DownloadStatus status;
  final int downloadedBytes;
  final double progress;
  final String? errorMessage;
  final DateTime createdTime;
  final DateTime? startTime;
  final DateTime? completedTime;

  DownloadTask({
    required this.id,
    required this.fileName,
    required this.cloudId,
    required this.downloadUrl,
    required this.savePath,
    required this.fileSize,
    required this.md5,
    this.status = DownloadStatus.pending,
    this.downloadedBytes = 0,
    this.progress = 0.0,
    this.errorMessage,
    required this.createdTime,
    this.startTime,
    this.completedTime,
  });

  factory DownloadTask.fromJson(Map<String, dynamic> json) =>
      _$DownloadTaskFromJson(json);

  Map<String, dynamic> toJson() => _$DownloadTaskToJson(this);

  factory DownloadTask.fromMap(Map<String, dynamic> map) {
    return DownloadTask(
      id: map['id'] ?? '',
      fileName: map['file_name'] ?? '',
      cloudId: map['cloud_id'] ?? '',
      downloadUrl: map['download_url'] ?? '',
      savePath: map['save_path'] ?? '',
      fileSize: map['file_size']?.toInt() ?? 0,
      md5: map['md5'] ?? '',
      status: DownloadStatus.values.firstWhere(
        (e) => e.toString().split('.').last == map['status'],
        orElse: () => DownloadStatus.pending,
      ),
      downloadedBytes: map['downloaded_bytes']?.toInt() ?? 0,
      progress: map['progress']?.toDouble() ?? 0.0,
      errorMessage: map['error_message'],
      createdTime: DateTime.parse(map['created_time'] ?? DateTime.now().toIso8601String()),
      startTime: map['start_time'] != null ? DateTime.parse(map['start_time']) : null,
      completedTime: map['completed_time'] != null ? DateTime.parse(map['completed_time']) : null,
    );
  }

  Map<String, dynamic> toMap() {
    return {
      'id': id,
      'file_name': fileName,
      'cloud_id': cloudId,
      'download_url': downloadUrl,
      'save_path': savePath,
      'file_size': fileSize,
      'md5': md5,
      'status': status.toString().split('.').last,
      'downloaded_bytes': downloadedBytes,
      'progress': progress,
      'error_message': errorMessage,
      'created_time': createdTime.toIso8601String(),
      'start_time': startTime?.toIso8601String(),
      'completed_time': completedTime?.toIso8601String(),
    };
  }

  DownloadTask copyWith({
    String? id,
    String? fileName,
    String? cloudId,
    String? downloadUrl,
    String? savePath,
    int? fileSize,
    String? md5,
    DownloadStatus? status,
    int? downloadedBytes,
    double? progress,
    String? errorMessage,
    DateTime? createdTime,
    DateTime? startTime,
    DateTime? completedTime,
  }) {
    return DownloadTask(
      id: id ?? this.id,
      fileName: fileName ?? this.fileName,
      cloudId: cloudId ?? this.cloudId,
      downloadUrl: downloadUrl ?? this.downloadUrl,
      savePath: savePath ?? this.savePath,
      fileSize: fileSize ?? this.fileSize,
      md5: md5 ?? this.md5,
      status: status ?? this.status,
      downloadedBytes: downloadedBytes ?? this.downloadedBytes,
      progress: progress ?? this.progress,
      errorMessage: errorMessage ?? this.errorMessage,
      createdTime: createdTime ?? this.createdTime,
      startTime: startTime ?? this.startTime,
      completedTime: completedTime ?? this.completedTime,
    );
  }

  bool get isCompleted => status == DownloadStatus.completed;
  bool get isFailed => status == DownloadStatus.failed;
  bool get isDownloading => status == DownloadStatus.downloading;
  bool get isPending => status == DownloadStatus.pending;
  bool get isCancelled => status == DownloadStatus.cancelled;
  bool get isPaused => status == DownloadStatus.paused;

  Duration? get downloadDuration {
    if (startTime == null) return null;
    final endTime = completedTime ?? DateTime.now();
    return endTime.difference(startTime!);
  }

  double get downloadSpeed {
    final duration = downloadDuration;
    if (duration == null || duration.inSeconds == 0) return 0.0;
    return downloadedBytes / duration.inSeconds; // bytes per second
  }

  @override
  String toString() {
    return 'DownloadTask(id: $id, fileName: $fileName, cloudId: $cloudId, downloadUrl: $downloadUrl, savePath: $savePath, fileSize: $fileSize, md5: $md5, status: $status, downloadedBytes: $downloadedBytes, progress: $progress, errorMessage: $errorMessage, createdTime: $createdTime, startTime: $startTime, completedTime: $completedTime)';
  }

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) return true;
    return other is DownloadTask && other.id == id;
  }

  @override
  int get hashCode => id.hashCode;
}